package jgo.plugins.server;

import java.nio.charset.StandardCharsets;

import com.alibaba.fastjson.JSON;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpVersion;
import jgo.Context;
import jgo.JGo;
import jgo.User;
import jgo.core.ServerPlugin;

public class NettyHttpServerPlugin extends ServerPlugin {
	private EventLoopGroup bossGroup;
	private EventLoopGroup workerGroup;
	private Channel serverChannel;

	public NettyHttpServerPlugin(int port) {
		super(port);
	}

	@Override
	public void init(JGo gin) {
		// 初始化逻辑（如果需要）
	}

	@Override
	public void start(JGo gin) {
		bossGroup = new NioEventLoopGroup(1);
		workerGroup = new NioEventLoopGroup();

		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
					.childHandler(createChannelInitializer(gin));

			serverChannel = b.bind(port).sync().channel();
			System.out.println("Netty HTTP server started on port " + port);
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
			throw new RuntimeException("Failed to start Netty server", e);
		}
	}

	private ChannelInitializer<SocketChannel> createChannelInitializer(JGo gin) {
		return new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel ch) {
				ch.pipeline().addLast(new HttpServerCodec(), new HttpObjectAggregator(65536),
						new SimpleChannelInboundHandler<FullHttpRequest>() {
							@Override
							protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
								Context httpContext = new NettyHttpContext(ctx, request);
								gin.handleRequest(httpContext);
							}
						});
			}
		};
	}

	@Override
	public void stop() {
		if (serverChannel != null) {
			serverChannel.close();
		}
		if (bossGroup != null) {
			bossGroup.shutdownGracefully();
		}
		if (workerGroup != null) {
			workerGroup.shutdownGracefully();
		}
	}

	private static class NettyHttpContext extends HttpContext {
		private final ChannelHandlerContext ctx;
		private final FullHttpRequest request;
		private int statusCode = 200;
		private boolean responseSent = false;

		public NettyHttpContext(ChannelHandlerContext ctx, FullHttpRequest request) {
			this.ctx = ctx;
			this.request = request;
		}

		@Override
		public String getMethod() {
			return request.method().name();
		}

		@Override
		public String getPath() {
			return request.uri();
		}

		@Override
		public Context status(int statusCode) {
			this.statusCode = statusCode;
			return this;
		}

		@Override
		public Context json(Object obj) {
			String jsonResponse = JSON.toJSONString(obj);
			byte[] responseBytes = jsonResponse.getBytes(StandardCharsets.UTF_8);
			FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
					HttpResponseStatus.valueOf(statusCode), Unpooled.wrappedBuffer(responseBytes));
			response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json");
			response.headers().set(HttpHeaderNames.CONTENT_LENGTH, responseBytes.length);
			ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
			responseSent = true;
			return this;
		}

		@Override
		public Context header(String name, String value) {
			// 在 Netty 中，我们通常在发送响应时设置头部
			// 这里我们可以存储头部信息，在发送响应时使用

			return this;
		}

		public void end() {
			if (!responseSent) {
				FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
						HttpResponseStatus.valueOf(statusCode));
				ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
				responseSent = true;
			}
		}

		public String body() {
			return request.content().toString(StandardCharsets.UTF_8);
		}

		public boolean isResponseSent() {
			return responseSent;
		}

	}

}